#include <string.h>
#include <stdio.h>

#include "xml.h"
#include "utils.h"
#include "common.h"

#define STR_END '\0'

#define XML_BUF_SIZE (4*1024)
static char xml_buf[XML_BUF_SIZE];

char *pmem_start;
char *pmem_cur;
char *pmem_end;

static char *del_space(char *str);
static bool is_char(int c);
static x_node *xml_add_subnode(x_node *root,x_str *xstr);
static void xml_add_val(x_node *node,x_str *str);
static int xml_add_attri(x_node *node,x_str *str);
static int xml_read_node_value(char *org,x_str *val,char **lest);
static x_node *xml_parse_node(x_str *val);

void *xml_malloc(int size)
{
    char *tmp;
    
    if((pmem_end - pmem_cur) > size){
        tmp = pmem_cur;
        pmem_cur +=size;
        return (void*)tmp;
    }
    return NULL;
}

void xml_mem_reset(void)
{
    memset(xml_buf,0,XML_BUF_SIZE);
    
    pmem_start = xml_buf;
    pmem_end = pmem_start + (XML_BUF_SIZE -1);
    pmem_cur = pmem_start;
}

void xml_mfree(int size)
{
    if((pmem_cur - pmem_start) > size){
        pmem_cur -= size;
    }
}

static char *del_space(char *str)
{
    if((NULL == str) || (STR_END == *str)){
        return NULL;
    }
    
    while(STR_END != *str){
        switch(*str){
            case SPACE:
            case TAB:
            case '\r':
            case '\n':
            str++;
            break;
            default:
                return str;
         }
     }
     return NULL;   
}

static bool is_char(int c)
{
    return (bool)((c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || (c == '-'));
}

static x_node *xml_add_subnode(x_node *root,x_str *xstr)
{
    x_node *tmp;
    x_node *node;

    node = xml_parse_node(xstr);
    printf("xml_add_subnode,name=%s,len=%d\n",node->name.text,node->name.len);  
    if(NULL != node->attri_list)
    {
        printf("attr->name:value=%s:%s\n",node->attri_list->name.text,
               node->attri_list->value.text);
        printf("attri->sibling->name:value=%s:%s\n",
               node->attri_list->sibling->name.text,node->attri_list->sibling->value.text);    
    }
    if(NULL == node){
        printf("error,NULL\n");
        return NULL;
    }

    if(root->child == NULL){
        printf("add child,root->name:%s\n",root->name.text);
        root->child = node;
    }else{
      tmp = root->child;
      while(NULL != tmp->sibling){
        printf("sibling->name:%s\n",tmp->sibling->name.text);
        tmp = tmp->sibling;
      }
      printf("sibling->name:%s\n",tmp->name.text);
      tmp->sibling = node;
   }
  node->parent = root;
  return node;
}

static void xml_add_val(x_node *node,x_str *str)
{
    node->value.text = str->text;
    node->value.len = str->len;
}

static int xml_add_attri(x_node *node,x_str *str)
{
    x_attri *attri = NULL;
    x_attri *tmp = NULL;
    int num = 0;;
    char *p = NULL;
    char *p1 = NULL;
    char *p2 = NULL;

    p = str->text;
    node->attri_list = NULL;
    num = 0;
    while(STR_END != *p){
        p = del_space(p);
        p1 = strchr(p,'=');

        printf("p1:%s\n",p1);
        if(NULL == p1){
            break;
        }else{
            if('"' != *(p1 + 1)){
                break;
            }

            *p1 = STR_END;
            p2 = strchr(p1 + 2,'"');
            if(NULL == p2){
                return -1;
            }
            
            *p2 = STR_END;
            tmp = xml_malloc(sizeof(x_attri));
            memset(tmp,0,sizeof(x_attri));

            tmp->name.text = p;
            tmp->name.len = p1 - p;
            tmp->value.text = p1 + 2;
            tmp->value.len = p2 - (p1 + 2);
            printf("p:%s,p1+2:%s\n",p,p1+2);
            
            if(num == 0){
                node->attri_list = tmp;
            }else{
                attri->sibling = tmp;
            }
            attri = tmp;
            p = p2 + 1;
            num++;
        }       
    }

    printf("xml_add_attri exit,num=%d\n",num);
    return num;
}

static int xml_read_node_value(char *org,x_str *val,char **lest)
{
    char *p,*p1,*p2;
    int ivtype = XML_NONE;

    p = del_space(org);
    //printf("xml_read_node_value %s\n",p);
    
    if(NULL == p){/*end of xml*/
        goto OKExit;
    }

    if(*p != '<'){//value
        p1 = p;
        p2 = strchr(p1,'<');
        if(NULL == p2){
            goto ErrExit;
        }

        p = p2;
        val->text = p1;
        val->len = p2 - p1;
        ivtype = XML_DATA;
        goto OKExit;
    }

    /*<*/
    p++;/*skip '<'*/
    if(*p == '/'){/*end*/
        p1 = p + 1;
        p2 = strchr(p1,'>');

        if(p2 == NULL){/*error*/
            goto ErrExit;
        }

        ivtype = XML_NEND;
        *p2 = STR_END;
        p = p2 + 1;
        val->text = p1;
        val->len = p2 -p1;
     }else if(is_char(*p)){
        p1 = p;
        p2 = strchr(p1,'>');
        if(NULL == p2){
            goto ErrExit;
        }

        if(*(p2 - 1) == '/'){/*empty < abc/>*/
            ivtype = XML_NFULL;
            *(p2 - 1) = STR_END;

            val->text =  p1;
            val->len = (p2 - 1) - p1;

            p2++;
            p = p2;
         }else{
            ivtype = XML_NBEGIN;
            val->text = p1;
            val->len = p2 - p1;
            *p2 = STR_END;
            p2++;
            p = p2;
         }
     }
     else if(*p == '!'){
        if(*(p + 1) == '-' && *(p + 2) == '-')/*comment*/
        {
            p+=3;
            p1 = del_space(p);
            p2 = strstr(p1,"-->");

            if(p2 == NULL){
                goto ErrExit;
            }else{
                ivtype = XML_COMMENT;
                val->text = p1;
                val->len = p2 - p1;
                *p2 = STR_END;
                p2 += 3;
                p = p2;
            }
         }
     }
OKExit:
    *lest = p;
    return (ivtype);

ErrExit:
    *lest = p;
    ivtype = XML_ERROR;
    return (ivtype);
}

static x_node *xml_parse_node(x_str *val)
{
    char *p,*p1;
    x_node *node;

    printf("xml_parse_node,text=%s,len=%d\n",val->text,val->len);
    p = del_space(val->text);
    p1 = p;

    while(STR_END != *p1){
        if((SPACE == *p1) || (TAB == *p1)){
            break;
        }
        p1++;
    }

    *p1 = STR_END;
    if(p1 > p){
        node = (x_node *)xml_malloc(sizeof(x_node));
        memset(node,0,sizeof(x_node));
        node->name.text = p;
        node->name.len = p1 - p;
    }else{
        printf("error,NULL\n");
        return NULL;
    }

    p1++;
    if((p1 - p) < val->len){
        x_str attr_val;
        p1 = del_space(p1);
        if(NULL == p1){
            return node;
        }
        attr_val.text = p1;
        attr_val.len = val->len - (p1 -p);
        if(-1 == xml_add_attri(node,&attr_val)){
            printf("error,NULL\n");
            return NULL;
        }
    }

    return node;
}

x_node *xml_parse_read(char *xmlbuf,int xmlsize)
{
    char *p;
    x_str *xstr;
    x_str xstr_buf;
    x_node tmp_node;
    x_node *node;
    x_node *root;
    x_node *top;

    int vtype = XML_NONE;

    p = xmlbuf;
    p += 3;/*remove file head 'EF BB BF'*/
    if(strncmp(p,"<?xml version=",14) != 0){
        printf("Not xml file\n");
        return NULL;
    }

    p += 14;
    p = strstr(p,"?>");

    if(NULL == p){
        printf("xml file is wrong\n");
        return NULL;
    }

    p = del_space(p + 2);
    if(NULL == p){
        return NULL;
    }

    xml_mem_reset();

    node = &tmp_node;
    memset(node, 0,sizeof(x_node));

    root = node;
    xstr = &xstr_buf;
    top = NULL;

    while((NULL != p) && (STR_END != *p)){
        vtype = xml_read_node_value(p,xstr,&p);
        
        if(XML_NONE == vtype){
            break;
        }
        
        switch(vtype){
        case XML_NBEGIN:
            {
                printf("XML_NBEGIN,text=%s,len=%d\n",xstr->text,xstr->len);
                node = xml_add_subnode(node,xstr);
                if(NULL == node){
                    printf("error,NULL\n");
                    return NULL;
                }

                if(NULL != top){
                    if(node->parent == top->parent){
                        return NULL;
                    }
                }else{
                    top = node;
                }
                    
            }
            break;        
        case XML_DATA:
            {
                printf("XML_DATA,text=%s,len=%d\n",xstr->text,xstr->len);
                xml_add_val(node,xstr);
            }
            break;
        case XML_NFULL:
            {
                printf("XML_NFULL,text=%s,len=%d\n",xstr->text,xstr->len);
                printf("node->name=%s\n",node->name.text);
                node = xml_add_subnode(node,xstr);
                if(NULL == node){
                    return NULL;
                }

                if(NULL != top){
                    if(node->parent == top->parent){
                        return NULL;
                    }
                }else{
                    top = node;
                }
                node = node->parent;
            }
            break;
        case XML_NEND:
            {
                printf("XML_NEND\n");
                node = xml_get_parent(node);
                if(node == NULL){
                    return NULL;
                }
            }
            break;
        case XML_ERROR:
            {
                printf("XML_ERROR\n");
                return NULL;
            }
            break;
        case XML_COMMENT:
            {
                printf("XML_COMMNET\n");
            }
            break;
        default:
            break;
        }
    }

    if(root != node){/*node beging mismatch end*/
        printf("root != node\n");
        return NULL;
    }

    return root->child;
}

char *xml_get_attribyname(x_node *node,char *name)
{
    x_attri *list = node->attri_list;
    while(NULL != list){
        if(strncmp(list->name.text,name,list->name.len) == 0){
            return list->value.text;
        }
        list = list->sibling;
    }
    return NULL;
}

x_node *xml_get_nodebyname(x_node *node,char *name)
{
    while(NULL != node){
        if(strncmp(node->name.text,name,node->name.len) == 0){
            return node;
        }
        node = node ->sibling;
    }

    return NULL;
}

x_node *xml_get_parent(x_node *sub)
{
    if(NULL != sub){
        return sub->parent;
    }
    return NULL;
}

x_node *xml_get_subnode(x_node *parent)
{
    if(NULL != parent){
        return parent->child;
    }
    return NULL;
}

char *xml_get_node_name(x_node *node)
{
    while(NULL != node){
        return node->name.text;
    }

    return NULL;
}
